#include "client.h"

int isValidCommand(const char* cmd){
    if(strcmp("ls",cmd)==0){
        return 1;
    }
    else if(strcmp("cd",cmd)==0){
        return 1;
    }
    else if(strcmp("pwd",cmd)==0){
        return 1;
    }
    else if(strcmp("mkdir",cmd)==0){
        return 1;
    }
    else if(strcmp("rmdir",cmd)==0){
        return 1;
    }
    else if(strcmp("puts",cmd)==0){
        return 1;
    }
    else if(strcmp("gets",cmd)==0){
        return 1;
    }
    else if(strcmp("remove",cmd)==0){
        return 1;
    }
    return 0;
}

int getCommandType(const char *cmd,commandPacket_t* pcommandPacket){
    if(strcmp("ls",cmd)==0){
        pcommandPacket->cmd=LSCOMMAND;
    }
    else if(strcmp("cd",cmd)==0){
        pcommandPacket->cmd=CDCOMMAND;
    }
    else if(strcmp("pwd",cmd)==0){
        pcommandPacket->cmd=PWDCOMMAND;
    }
    else if(strcmp("mkdir",cmd)==0){
        pcommandPacket->cmd=MKDIRCOMMAND;
    }
    else if(strcmp("rmdir",cmd)==0){
        pcommandPacket->cmd=RMDIRCOMMAND;
    }
    else if(strcmp("puts",cmd)==0){
        pcommandPacket->cmd=PUTSCOMMAND;
    }
    else if(strcmp("gets",cmd)==0){
        pcommandPacket->cmd=GETSCOMMAND;
    }
    else if(strcmp("remove",cmd)==0){
        pcommandPacket->cmd=REMOVECOMMAND;
    }
    return 0;

}

int recvn(int sockfd,void *buf,int length){
    ssize_t cur_count=0;
    char *p=(char *)buf;
    while(cur_count<length){
        ssize_t sret=recv(sockfd,p+cur_count,length-cur_count,0);
        cur_count+=sret;
    }
    return 0;
}

int setCommandPacket(commandPacket_t* pcommandPacket,const char *cmd,const char *arg){
    getCommandType(cmd,pcommandPacket);                                              
    if(arg==NULL){
       pcommandPacket->length=1;
       strcpy(pcommandPacket->argument,".");
    }
    else{
        pcommandPacket->length=strlen(arg);
        strcpy(pcommandPacket->argument,arg);
    }
    return 0;
}

/* Usage:  */
//上传
int transFile(int sockfd, char *filename) {
    train_t train;
    int fd = open(filename, O_RDWR);
    ERROR_CHECK(fd, -1, "open transFile");

    struct stat statbuf;
    fstat(fd, &statbuf);
    printf("filesize = %ld\n", statbuf.st_size);
    off_t filesize = statbuf.st_size;

    // 1. 发送文件总大小
    train.length = sizeof(filesize);
    memcpy(train.data, &filesize, sizeof(off_t));
    send(sockfd, &train, sizeof(train.length) + train.length, MSG_NOSIGNAL);

    // 2. 接收客户端已接收的大小
    off_t received_size = 0;
    recvn(sockfd, &train.length, sizeof(train.length));
    if (train.length == sizeof(off_t)) {
        recvn(sockfd, train.data, train.length);
        memcpy(&received_size, train.data, train.length);
        printf("Client has received %ld bytes, resuming from this point\n", received_size);
    }

    // 建立映射区
    char *p = (char *)mmap(NULL, filesize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    ERROR_CHECK(p, MAP_FAILED, "mmap");

    off_t offset = received_size;
    while (1) {
        if (offset >= filesize) {
            break;
        }

        if (filesize - offset > 1000) {
            train.length = 1000;
        } else {
            train.length = filesize - offset;
        }

        send(sockfd, &train.length, sizeof(train.length), MSG_NOSIGNAL);
        send(sockfd, p + offset, train.length, MSG_NOSIGNAL);
        offset += train.length;
    }

    // 发送结束标志
    train.length = 0;
    send(sockfd, &train.length, sizeof(train.length), MSG_NOSIGNAL);

    munmap(p, filesize);
    close(fd);
    return 0;
}


//下载
int putsFile(int sockfd, char *filename) {
    train_t train;

    // 1. 接收文件总大小
    recvn(sockfd, &train.length, sizeof(train.length));
    recvn(sockfd, train.data, train.length);
    off_t filesize;
    memcpy(&filesize, train.data, train.length);
    printf("filesize = %ld\n", filesize);

    // 检查本地文件是否存在及已下载大小
    off_t local_size = 0;
    struct stat statbuf;
    if (stat(filename, &statbuf) == 0) {
        local_size = statbuf.st_size;
        printf("Local file exists, size = %ld\n", local_size);
    }

    // 2. 发送已接收的大小给服务端
    train.length = sizeof(local_size);
    memcpy(train.data, &local_size, train.length);
    send(sockfd, &train, sizeof(train.length) + train.length, MSG_NOSIGNAL);

    // 打开文件，如果是续传则追加写入
    int flags = O_WRONLY | O_CREAT;
    if (local_size > 0) {
        flags |= O_APPEND;
        printf("Resuming download from %ld\n", local_size);
    } else {
        flags |= O_TRUNC;
    }

    int fd = open(filename, flags, 0666);
    ERROR_CHECK(fd, -1, "open putsfile");

    off_t cursize = local_size;
    off_t lastsize = local_size;
    off_t slice = filesize / 10000;

    while (1) {
        bzero(train.data, sizeof(train.data));
        recvn(sockfd, &train.length, sizeof(train.length));

        if (train.length == 0) {
            break;
        }

        recvn(sockfd, train.data, train.length);
        write(fd, train.data, train.length);

        cursize += train.length;
        if (cursize - lastsize > slice) {
            printf("%5.2lf%%\r", 100.0 * cursize / filesize);
            fflush(stdout);
            lastsize = cursize;
        }
    }

    printf("100.00%%\n");
    close(fd);
    return 0;
}


